/**
 * 
 */
package idv.takeshi.software.productline.bookstore.presentation.web.controller;

import java.util.HashMap;
import java.util.Locale;

import java.util.Map;

import idv.takeshi.software.productline.bookstore.application.service.MaintainCategoriesService;
import idv.takeshi.software.productline.bookstore.application.service.CanNotRemoveCategoryException;
import idv.takeshi.software.productline.bookstore.domain.model.category.BookAlreadyAttachedException;
import idv.takeshi.software.productline.bookstore.domain.model.category.Category;
import idv.takeshi.software.productline.bookstore.domain.model.category.CategoryException;
import idv.takeshi.software.productline.bookstore.domain.model.category.CategoryNameNotSetException;
import idv.takeshi.software.productline.bookstore.domain.model.valueobject.InternationalText;
import idv.takeshi.software.productline.bookstore.domain.model.valueobject.LocaleNotNeededException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
import org.springframework.context.MessageSource;

/**
 * A controller for maintaining Category models.
 * @author takeshi
 *
 */
@Controller("maintainCategoriesController")
@RequestMapping("/categories")
public class MaintainCategoriesController {
	
	private static final String UNKONW_EXCEPTION_MSG = "Unkonw exception occurs";

	private static final int STATUS_CODE_UNKNOWN_ERROR = 5;
	
	private static final int STATUS_CODE_CATEGORY_ID_SETTING_ERROR = 4;

	private static final int STATUS_CODE_CATEGORY_NAME_SETTING_ERROR = 3;

	private static final int STATUS_CODE_BOOKS_ATTACHED = 2;

	private static final int STATUS_CODE_CATEGORY_NAME_NOT_SET = 1;

	private static final int STATUS_CODE_OK = 0;

	private static final String STATUS_MESSAGE_KEY = "statusMessage";

	private static final String STATUS_CODE_KEY = "statusCode";

	private static final Log log = LogFactory.getLog(MaintainCategoriesController.class);
	
	@Autowired
	private MessageSource messageSource;
	
	@Autowired
	private MaintainCategoriesService maintainCategoriesService;
	
	@RequestMapping(value = "/maintainCategories.html", method = RequestMethod.GET)
	public void getRootCategory(ModelMap modelMap){
		Category rootCategory = this.maintainCategoriesService.getRootCategory();
		modelMap.addAttribute("rootCategory", rootCategory);
		modelMap.addAttribute("neededLocales", InternationalText.getNeededLocales());
	}
	
	@RequestMapping(value = "/addNewCategory.html", method = RequestMethod.POST)
	public @ResponseBody Map<String, Object> addNewCategory(
			WebRequest request, Locale locale){
		Map<String, Object> responseMap = new HashMap<String, Object>();
		String parentCategoryId = request.getParameter("categoryId");
		Long newCategoryId = null;
		InternationalText iText = null;
		
		responseMap.put(STATUS_CODE_KEY, STATUS_CODE_OK);
		responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
				"categories.maintainCategories.category.add.successfully", null, locale));
		
		iText = new InternationalText();
		try {
			setITextValus(request, iText);
			
			newCategoryId = this.maintainCategoriesService.addNewCategory(
					Long.valueOf(parentCategoryId), iText);
		} catch (CategoryNameNotSetException e) {
			log.warn("iText:" + iText + " not totally set", e);
			responseMap.put(STATUS_CODE_KEY, STATUS_CODE_CATEGORY_NAME_NOT_SET);
			responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
					"categories.maintainCategories.category.nameNotSet", null, locale));
		} catch(BookAlreadyAttachedException e){
			log.warn("parentCategory:" + parentCategoryId + " already has books attached", e);
			responseMap.put(STATUS_CODE_KEY, STATUS_CODE_BOOKS_ATTACHED);
			responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
					"categories.maintainCategories.category.hasBooksAttached", 
					new Object[]{parentCategoryId}, locale));
		} catch (LocaleNotNeededException e) {
			log.warn("iText:" + iText + " setting error", e);
			responseMap.put(STATUS_CODE_KEY, STATUS_CODE_CATEGORY_NAME_SETTING_ERROR);
			responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
					"categories.maintainCategories.category.nameSettingError", null, locale));
		} catch (NumberFormatException e) {
			String msg = "parentCategoryId:" + parentCategoryId + " setting error";
			log.error(msg, e);
			responseMap.put(STATUS_CODE_KEY, STATUS_CODE_CATEGORY_ID_SETTING_ERROR);
			responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
					"categories.maintainCategories.category.parentCategoryIdError", 
					new Object[]{parentCategoryId}, locale));
		} catch (Exception e) {
			log.error(UNKONW_EXCEPTION_MSG, e);
			responseMap.put(STATUS_CODE_KEY, STATUS_CODE_UNKNOWN_ERROR);
			responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
					"categories.maintainCategories.category.unknownError", null, locale));
		}
		
		responseMap.put("newCategoryId", newCategoryId);
		
		return responseMap;
	}

	@RequestMapping(value = "/updateCategory.html", method = RequestMethod.POST)
	public @ResponseBody Map<String, Object> updateCategory(
			WebRequest request, Locale locale){
		
		Map<String, Object> responseMap = new HashMap<String, Object>();
		String categoryId = request.getParameter("categoryId");
		InternationalText newIText = new InternationalText();
		
		responseMap.put(STATUS_CODE_KEY, STATUS_CODE_OK);
		responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
				"categories.maintainCategories.category.update.successfully", null, locale));
		try{
			setITextValus(request, newIText);
			this.maintainCategoriesService.updateCategory(Long.valueOf(categoryId), newIText);
		} catch (LocaleNotNeededException e) {
			log.warn("iText:" + newIText + " setting error", e);
			responseMap.put(STATUS_CODE_KEY, STATUS_CODE_CATEGORY_NAME_SETTING_ERROR);
			responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
					"categories.maintainCategories.category.nameSettingError", null, locale));
		} catch (Exception e) {
			log.error(UNKONW_EXCEPTION_MSG, e);
			responseMap.put(STATUS_CODE_KEY, STATUS_CODE_UNKNOWN_ERROR);
			responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
					"categories.maintainCategories.category.unknownError", null, locale));
		}
		return responseMap;
	}
	
	@RequestMapping(value = "/removeCategory.html", method = RequestMethod.POST)
	public @ResponseBody Map<String, Object> removeCategory(
			WebRequest request, Locale locale){
		
		Map<String, Object> responseMap = new HashMap<String, Object>();
		String categoryId = request.getParameter("categoryId");
		
		responseMap.put(STATUS_CODE_KEY, STATUS_CODE_OK);
		responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
				"categories.maintainCategories.category.remove.successfully", null, locale));
		
		try{
			this.maintainCategoriesService.removeCategory(Long.valueOf(categoryId));
		} catch(CanNotRemoveCategoryException e){
			log.warn("can not remove category:" + categoryId + " due to it has books attached with it", e);
			responseMap.put(STATUS_CODE_KEY, STATUS_CODE_BOOKS_ATTACHED);
			responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
					"categories.maintainCategories.category.hasBooksAttached", 
					new Object[]{categoryId}, locale));
		} catch(Exception e){
			log.error(UNKONW_EXCEPTION_MSG, e);
			responseMap.put(STATUS_CODE_KEY, STATUS_CODE_UNKNOWN_ERROR);
			responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
					"categories.maintainCategories.category.unknownError", null, locale));
		}
		return responseMap;
	}
	
	@RequestMapping(value = "/moveCategory.html", method = RequestMethod.POST)
	public @ResponseBody Map<String, Object> moveCategory(
			WebRequest request, Locale locale){
		
		Map<String, Object> responseMap = new HashMap<String, Object>();
		String categoryId = request.getParameter("categoryId");
		String newParentCategoryId = request.getParameter("parentCategoryId");
		String newSequence = request.getParameter("sequence");
		
		responseMap.put(STATUS_CODE_KEY, STATUS_CODE_OK);
		responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
				"categories.maintainCategories.category.move.successfully", null, locale));
		
		try{
			this.maintainCategoriesService.moveCategory(Long.valueOf(
					newParentCategoryId), Long.valueOf(categoryId), Integer.valueOf(newSequence));
		} catch(BookAlreadyAttachedException e){
			log.warn("parentCategory:" + newParentCategoryId + " already has books attached", e);
			responseMap.put(STATUS_CODE_KEY, STATUS_CODE_BOOKS_ATTACHED);
			responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
					"categories.maintainCategories.category.hasBooksAttached", 
					new Object[]{newParentCategoryId}, locale));
		} catch(Exception e){
			log.error(UNKONW_EXCEPTION_MSG, e);
			responseMap.put(STATUS_CODE_KEY, STATUS_CODE_UNKNOWN_ERROR);
			responseMap.put(STATUS_MESSAGE_KEY, this.messageSource.getMessage(
					"categories.maintainCategories.category.unknownError", null, locale));
		}
		return responseMap;
	}

	/**
	 * @param request
	 * @param iText
	 * @throws LocaleNotNeededException
	 */
	private void setITextValus(WebRequest request, InternationalText iText)
			throws LocaleNotNeededException {
		for(String localeName : InternationalText.getNeededLocales()){
			iText.setText(localeName, "" + request.getParameter(localeName));
		}
	}

}
